1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.data.ini;
12 public import hip.api.data.ini;
13 import hip.util.conv:to;
14 
15 
16 class IniFile : IHipIniFile
17 {
18     IniBlock[string] _blocks;
19     string path;
20     bool configFound = false;
21     bool _noError = true;
22     string[] errors;
23 
24     bool noError() const{return _noError;}
25     ref IniBlock[string] blocks(){return _blocks;}
26     const(string[]) getErrors() const{return cast(const)errors;}
27 
28     /**
29     *   Simple parser for the .conf or .ini files commonly found.
30     */
31     static IniFile parse(string content, string path = "")
32     {
33         IniFile ret = new IniFile();
34         ret.path = path;
35         if(content != "")
36             ret.configFound = true;
37 
38         for(int i = 0; i < content.length; i++)
39         {
40             char c = content[i];
41             if(c == ';' || c == '#')
42             {
43                 while(i < content.length && content[++i] != '\n'){}
44                 continue;
45             }
46             else if(c == '[')
47             {
48                 import hip.util.string : replaceAll, trim, splitRange;
49                 import hip.util.algorithm:put;
50                 string capture = "";
51                 while(i < content.length)
52                 {
53                     i++;
54                     if(i >= content.length)
55                         return ret;
56                     else if(content[i] == ']')
57                         break;
58                     else
59                         capture~=content[i];
60                 }
61 
62                 IniBlock block;
63                 block.name = capture;
64                 capture = "";
65                 //Read until finding a key.
66                 while(++i < content.length && (c = content[i]) != '['){capture~=c;}
67 
68                 
69                 foreach(l; capture.splitRange("\n"))
70                 {
71                     l = l.trim;
72                     if(l == "")
73                         continue;
74                     string k, v;
75                     l.splitRange("=").put(&k, &v);
76                     if(v == "")
77                     {
78                         ret.errors~= "No value for key '"~k~"'";
79                         ret._noError = false;
80                         break;
81                     }
82                     string name = k.replaceAll(' ', "");
83                     block.vars[name] =  IniVar(name, formatValue(v));
84                 }
85                 ret.blocks[block.name] = block;
86                 i--;
87             }
88         }
89         return ret;
90     }
91 
92     IniVar* getIniVar(string varPath)
93     {
94         import hip.util.string:splitRange;
95         import hip.util.algorithm;
96         string accessorA, accessorB;
97         varPath.splitRange(".").put(&accessorA, &accessorB);
98         if(accessorB == "")
99             return null;
100         IniBlock* b = (accessorA in blocks);
101         if(b is null)
102             return null;
103         return (accessorB in *b);
104     }
105 }
106 
107 /**
108 *   Remove comments and spaces from the Value from KeyValue pair
109 */
110 private string formatValue(string unformattedValue)
111 {
112     string ret;
113     foreach(ch; unformattedValue)
114     {
115         if(ch == '#' || ch == ';') //Remove comments
116             break;
117         if(ch == ' ') //Remove spaces for to!int and friends working correctly
118             continue;
119         ret~= ch;
120     }
121     return ret;
122 }